#include <map>
#include "VideoEncode.h"
#include "mediaPriv.h"
#include "Thread/Thread.h"
#include "ConfigApi.h"
#include "FormatVideo.h"

using namespace Fdt;
using namespace ConfigApi;
using namespace FrameCommon;
namespace Media{

/*
bool isRtspOut(int channel){
	return channel == RTSP_MAIN_OUT || channel == RTSP_SUB_OUT1
		|| channel == RTSP_SUB_OUT2 || channel == RTSP_SUB_OUT3
		|| channel == RTSP_SUB_OUT4 || channel == RTSP_SUB_OUT5
		|| channel == RTSP_SUB_OUT6 || channel == RTSP_SUB_OUT7
		|| channel == RTSP_SUB_OUT8;
}*/

//非特殊要求不开启其他rtsp输出，尤其是RTSP_SUB_OUT6~8会影响其他链路
bool isRtspOut(int channel){
	return channel == RTSP_MAIN_OUT;
}


std::map<int, VideoEncode *> VideoEncode::s_encoder_map;

VideoEncode *VideoEncode::instance(int channel){
	if(s_encoder_map.find(channel) == s_encoder_map.end()){
		s_encoder_map[channel] = new VideoEncode(channel);
	}

	return s_encoder_map[channel];
}


VideoEncode::VideoEncode(int channel){

	_channel = channel;
	Init();
}

bool VideoEncode::createEncoder(){
    int status;
    int screenMode;
    VideoEncCropCfg encCrop;
    VideoEncDesc encDesc;
    memset(&encDesc, 0, sizeof(encDesc));
    encDesc.card = 0;
    encDesc.srcs = 1;
    encDesc.channel = _channel;
    status = createVideoEncoder(&encDesc, &_videoEncoder);
    tracef("MEDIA_UvcSetformat channel %d\n", _channel);
    if (status)
    {
        tracef("create video encoder failed!");
        return false;
    }

    mediaCtl_g.pVideoEncoder[_channel] = _videoEncoder;
    return true;
}

bool VideoEncode::Init(){
	CurtainInfo curtainInfo;
	// if(CurtainActManager::instance()->getCurtainLayout(&curtainInfo)){
	// 	for(int i = 0; i < curtainInfo.outNum && i < sizeof(curtainInfo.cofigInfo); i++){
	// 		if(curtainInfo.cofigInfo[i].interface == _channel){
	// 			width = curtainInfo.cofigInfo[i].videoMixerInfo.displayWidth;
	// 			height = curtainInfo.cofigInfo[i].videoMixerInfo.displayHeight;
	// 			tracef(" width  %d, height %d\n", curtainInfo.cofigInfo[i].videoMixerInfo.displayWidth,
	// 				curtainInfo.cofigInfo[i].videoMixerInfo.displayHeight);
	// 		}
	// 	}
	// }

	if(!createEncoder()){
		errorf("VideoEncode create error! channel %d", _channel);
		return false;
	}
	tracef("_videoEncoder ptr %p", _videoEncoder);

    VideoEncFormat encFormat;
    memset(&encFormat, 0, sizeof(VideoEncFormat));
    encFormat.type = videoEncH264; //videoEncH265

    encFormat.width = _width;
    encFormat.height = _height;
    encFormat.bitrateControl = videoEncBitrateCtrlConstant;  
    encFormat.fps = 30;
    encFormat.gop = encFormat.fps*2;
    encFormat.bitrate = (1*encFormat.width*encFormat.height/1024)*encFormat.fps/30;
    encFormat.quality = 1;
    encFormat.qualityType = 0;
    encFormat.pack = videoEncPackTypeRAW;
    encFormat.profile = h264ProfileHigh;
	return setFormat(&encFormat);

    // VideoEncRefOptions refOptions;
    // memset(&refOptions, 0, sizeof(refOptions));
    
    // _format.type = videoEncH264;
    // _format.width = 1920;
    // _format.height = 1080;
    // _format.bitrateControl = videoEncBitrateCtrlConstant;
    // _format.fps = 30; //后续调整从配置读取

    // switch(_channel)
    // {
    //     case videoEncStreamMain:
    //         _format.bitrate = (1*_format.width*_format.height/1024)*_format.fps/5;
    //         break;
    //     case videoEncStreamExtra1:
    //         _format.bitrate = (1*_format.width*_format.height/1024)*_format.fps/30;
    //         break;   
    //     default:
    //         _format.bitrate = (1*_format.width*_format.height/1024)*_format.fps/30;
    //         break;
    // }

    // _format.gop = _format.fps*2;
    // _format.quality = 1;
    // _format.qualityType = 0;
    // _format.pack = videoEncPackTypeRAW;
    // _format.profile = h264ProfileHigh;
    // _format.svcType = videoSvcInvalid;
	// refOptions.mode = videoNoRefMode;
	// _format.refOptions = &refOptions;


	// LOG_PrintInfo("_channel=%d, bitrate %d, bitrateControl %d, fps %f!\n",
    //                _channel, _format.bitrate, _format.bitrateControl, 
    //                _format.fps);

    // return setFormat(&_format);
}


bool VideoEncode::getFormat(VideoEncFormat *format){
	if(!format)
		return false;
	memcpy(format, &_format, sizeof(VideoEncFormat));
	return true;
}

bool VideoEncode::setFormat(VideoEncFormat *format){

	if(!_videoEncoder || _videoEncoder->setFormat(_videoEncoder, format) < 0){
		tracef("setFomat failed");
	    return false;
	}
    return true;
}

bool VideoEncode::setIFrame(){
	if(!_videoEncoder || _videoEncoder->forceIFrame(_videoEncoder) < 0){
		tracef("forceIFrame failed");
	    return false;
	}
    return true;
	
}

bool VideoEncode::start(void *thiz, const FrameCommon::frame_callback_func& callback){
	std::lock_guard<std::recursive_mutex> guard(_mutex);
	/*if(!_config.empty()){
		setFormat(_config);
		_config.clear();
	}*/
	// 如果已经注册，则不用再次注册了
	if(_func_map.find(thiz) != _func_map.end()){

		return true;
	}

	_func_map[thiz] = std::make_shared<FrameCommon::frame_callback_func>(callback);
	if(!_started){
		_started =  start();
	}
	return _started;
}

bool VideoEncode::stop(void *thiz)
{
	std::lock_guard<std::recursive_mutex> guard(_mutex);
	if(_func_map.find(thiz) == _func_map.end()){
		return true;
	}
	_func_map.erase(thiz);
	if(_func_map.empty() && _started){
		_started = !stop();
		return !_started;
	}
	return true;
}


//extern "C" void rtsper_setVideoType(int type);
bool VideoEncode::start(){
	if(!_videoEncoder || _videoEncoder->start(_videoEncoder) < 0){
		errorf("start failed");
	    return false;
	}

	//rtsper_setVideoType((int)videoEncH264);
	//这里用this,编码器单例不退出,还有一些业务需要常驻,例如配置变更

	if(isRtspOut(_channel))
	{
	    _thread.start([this](Fdt::Thread *thread){this->ThreadProc(thread);});
	}
	return true;
}

bool VideoEncode::stop(){
    if(isRtspOut(_channel))
    {
	    _thread.stop();
	}

	if(!_videoEncoder || _videoEncoder->stop(_videoEncoder) < 0){
		tracef("stop failed");
	    return false;
	}

	return true;
}

bool VideoEncode::getPacket(SCREEN_vencPacket_t *pPacket)
{
    if(!_videoEncoder)
	{
        errorf("VideoEncode _videoEncoder is NULL\n");
        return false;
	}

    memset(&m_encPacket, 0, sizeof(m_encPacket));

    VideoEncBuf	videoBuf;
    
    m_encPacket.vBuffer = &videoBuf;
    m_encPacket.vSize = 1;
  
    int ret = _videoEncoder->getPacket(_videoEncoder, &m_encPacket);
    if(ret)
    {
        //errorf("VideoEncode getPacket failed");
        return false;
    }

    //media_FILE_Write3("wyg/getPacket.h264",m_encPacket.vBuffer[0].buffer,  m_encPacket.vBuffer[0].len);

    pPacket->pBuf = (char*)m_encPacket.vBuffer[0].buffer;

    pPacket->dataLen = m_encPacket.vBuffer[0].len;

	pPacket->pts = m_encPacket.pts;
    return true;
}

bool VideoEncode::releasePacket()
{
    if(!_videoEncoder || _videoEncoder->releasePacket(_videoEncoder) < 0){
		tracef("releasePacket failed");
	    return false;
	}

    return true;
}

void VideoEncode::ThreadProc(Fdt::Thread *thread)
{
    thread->setPriority(Fdt::PRIORITY_NORMAL, Fdt::POLICY_RR);

    while (thread->running())
    {
        int status;
        VideoEncPacket encPacket;
        VideoEncBuf	buffer;
        FrameCommon::CodecId encType; 

        memset(&encPacket, 0, sizeof(encPacket));
//        buffer.buffer = (unsigned char *)mediaCtl_g.pEncBuf;
//        encPacket.vBuffer = &buffer;
//        encPacket.vSize = 1;

        //buffer.buffer = (unsigned char *)mediaCtl_g.pEncBuf[VENC_RTSP_CHN];
        //buffer.len = len;
        encPacket.vBuffer = &buffer;
        encPacket.vSize = 1;

    	_mutex.lock();
    	while(_videoEncoder->getPacket(_videoEncoder, &encPacket) == 0){
    		_mutex.unlock();
            char *   ptr    = (char *)(encPacket.vBuffer[0].buffer);
        	size_t   size   = encPacket.vBuffer[0].len;
        	char *   pext   = (char *)encPacket.specBuffer;
        	size_t   exsize = encPacket.specLength;
        	uint64_t pts    = encPacket.pts;
        	bool     key    = (encPacket.type == videoEncFrameI)?true:false;

			if (encPacket.encType == videoEncH264)
			{
				encType = H264;
			}
			else if(encPacket.encType == videoEncH265)
			{
				encType = H265;
			}
			if(pts == 0){
				pts = getCurrentMillisecond();
			}

			auto framePtr = FrameCommon::Frame::CreateFrame(encType, ptr, size, pts, pts, key, 0);
        	_mutex.lock();
        	auto func_map = _func_map;
        	_mutex.unlock();

        	for(auto &func : func_map){
        		(*(func.second))(framePtr);
        	}

			_mutex.lock();
			_videoEncoder->releasePacket(_videoEncoder);
    	}

    	/*if(!_config.empty()){
    		_videoEncoder->stop(_videoEncoder);
    		setFormat(_config);
    		_config.clear();
    		_videoEncoder->start(_videoEncoder);
    	}*/

        _mutex.unlock();
        thread->sleep(5);
    }
}

bool VideoEncode::setFormat(const Json::Value &table){
    memset(&_format, 0, sizeof(VideoEncFormat));
    parseConfig(table, _format);
    return setFormat(&_format);
}

bool VideoEncode::onConfig(const Json::Value &newTable, const Json::Value &oldTable, int &result){
	{
		std::lock_guard<std::recursive_mutex> guard(_mutex);
		_config = newTable;
	}
#if 0
    memset(&_format, 0, sizeof(VideoEncFormat));
    parseConfig(newTable, _format);
    auto ret =  setFormat(&_format);
#endif
	return true;
}

bool VideoEncode::onConfigVerify(const Json::Value &newTable, const Json::Value &oldTable, int &result){

	int channel = 0;
	if(!Json::getJsonInt(newTable, "Chn", channel)){
		errorf("Chn error");
		return false;
	}
	if(channel != _channel){
		errorf("Chn error, channel %d, _channel %d", channel, _channel);
		return false;
	}

	int bitrate = 0;
	if(!Json::getJsonInt(newTable, "BitRate", bitrate)){
		errorf("BitRate error");
		return false;
	}
	if(!FormatVideo::verifyBitrate(bitrate)){
		errorf("bitrate error %d", bitrate);
		return false;
	}

	std::string Compression;
	if(!Json::getJsonString(newTable, "Compression", Compression)){
		errorf("Compression error");
		return false;
	}
	if(!FormatVideo::verifyCompression(Compression)){
		errorf("Compression error %s", Compression.data());
		return false;
	}

	int width = 0;
	int height = 0;
	if(!Json::getJsonInt(newTable, "Width", width)){
		errorf("Width error");
		return false;
	}
	if(!Json::getJsonInt(newTable, "Height", height)){
		errorf("Height error");
		return false;
	}
	if(!FormatVideo::verifyResolution(width, height)){
		errorf("Width %d, Height %d", width, height);
		return false;
	}

	float fps = 0.0;
	if(!Json::getJsonFloat(newTable, "Fps", fps)){
		errorf("Fps error");
		return false;
	}
	if(!FormatVideo::verifyFps(fps)){
		errorf("Fps error %.2f", fps);
		return false;
	}

	int gop = 0;
	if(!Json::getJsonInt(newTable, "GOP", gop)){
		errorf("GOP error");
		return false;
	}
	if(!FormatVideo::verifyGop(gop)){
		errorf("GOP error %d", gop);
		return false;
	}

	std::string profile;
	if(!Json::getJsonString(newTable, "Profile", profile)){
		errorf("Profile error");
		return false;
	}
	if(!FormatVideo::verifyProfile(profile)){
		errorf("profile error %s", profile.data());
		return false;
	}

	int quality = 0;
	if(!Json::getJsonInt(newTable, "Quality", quality)){
		errorf("Quality error");
		return false;
	}
	if(!FormatVideo::verifyGop(quality)){
		errorf("Quality error %d", quality);
		return false;
	}

	tracef("onConfigVerify success");
	return true;
}

void VideoEncode::parseConfig(const Json::Value& config, VideoEncFormat& format){
	if(!FormatVideo::EncType(config["Compression"].asString(), format.type)){
		tracef("_channel %d, Compression %s", _channel, config["Compression"].asCString());
	}

	format.width = config["Width"].asInt();
	format.height = config["Height"].asInt();

	tracef("format.width %d, format.height %d", format.width, format.height);

	if(!FormatVideo::BrcType(config["BitRateControl"].asString(), format.bitrateControl)){
		tracef("_channel %d, BitRateControl %s", _channel, config["BitRateControl"].asCString());
	}

	format.bitrate = config["BitRate"].asInt();
	format.fps = (float)(config["Fps"].asDouble());


	format.gop = config["GOP"].asInt();
	format.quality = config["Quality"].asInt();

	format.pack = videoEncPackTypeRAW;//直接设置有没有关系,有没有处理

	VideoEncH264Profile profile;
	if(!FormatVideo::Profile(config["Profile"].asString(), profile)){
		tracef("_channel %d, Profile %s", _channel, config["Profile"].asCString());
	}
	format.profile = (int)format.profile;


	tracef("VideoFormat");
	tracef("Width   %d", format.width);
	tracef("Height  %d", format.height);
	tracef("bitrate %d", format.bitrate);
	tracef("fps     %.1f", (float)(format.fps));
	tracef("gop     %d", format.gop);
	tracef("quality %d", format.quality);
	tracef("pack    %d", format.pack);
	tracef("profile %d", format.profile);
}

}//namespace Media


